import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

//server is also a table

public class MainServerThread extends Thread {
	
	private ServerSocket serverSocket;
	private List<Player> players;
	private Socket clientSocket;
	private int port;
	private Deck deck;
	
	private int pot,gRound;
	private int smallBlindBet;
	private int bigBlindBet;
	private int dealerId, smallBlindId, bigBlindId;
	private int chipsPerPlayer, showcards;
	private int auctionId;
	private boolean someoneIsChanging, madeBet, around;
	
	
	void playerBet(int playerIndex, int bet, boolean force, int type)
	{
		if (players.get(playerIndex).getChips()>bet) 
		{
			players.get(playerIndex).betChips(bet);
			pot += bet;
		}
		else
		{
			if (force == true)
			{
				playerAllin(playerIndex);
				return;
			}
		}
		players.get(playerIndex).setStatus(type);
	}
	void playerAllin(int playerIndex)
	{
		int all = players.get(playerIndex).allIn();
		pot+=all;
	}
	void playerCall(int playerIndex)
	{
		playerBet(playerIndex, currentBiggestBet()-players.get(playerIndex).getBet() ,false, 6);
	}
	private int currentBiggestBet()
	{
		int max = 0;
		for (int i=0; i<players.size(); i++)
		{
			if (players.get(i).getBet()>max && players.get(i).getStatus()!=3 && players.get(i).getStatus()!=4) max=players.get(i).getBet();
		}
		return max;
	}
	private boolean endOfAuction()
	{
		int max = currentBiggestBet();
		boolean end = true;
		int pl_count = 0;
		for (int i=0; i<players.size(); i++)
		{
			if (players.get(i).getAllin() == true || players.get(i).getFold() == true) pl_count++;
		}
		if (pl_count>=players.size()-1) return true;
		for (int i=0; i<players.size(); i++)
		{
			if ((players.get(i).getStatus()) != 3 && (players.get(i).getStatus() != 4))
			{
				if (players.get(i).getBet()<max) end = false;
			}
		}
		if (end == true && around == true) return true; else return false;
		
	}
	private int findBigBlind()
	{
		int r = 0;
		for (int i=0; i<players.size(); i++)
		{
			if (players.get(i).isBigBlind() == true)r = i;
		}
		return r;
	}
	private int findSmallBlind()
	{
		int r = 0;
		for (int i=0; i<players.size(); i++)
		{
			if (players.get(i).isSmallBlind() == true)r = i;
		}
		return r;
	}
	private int findDealer()
	{
		int r = 0;
		for (int i=0; i<players.size(); i++)
		{
			if (players.get(i).isDealer() == true)r = i;
		}
		return r;
	}
	public MainServerThread(int p, int pl, int sb, int bb, int chips)
	{
		port = p;
		deck = new Deck();
		smallBlindBet = sb;
		bigBlindBet = bb;
		chipsPerPlayer = chips;
		players = new ArrayList<Player>();
		showcards = 0;
	}
	private void sendEverythingToClients()
	{
		//sending information to every client about yourself and other players (chips, blinds etc)
		//first the server sends info to players about themselves
		//sending also what cards client has
		for (int i=0; i<players.size(); i++)
		{
			players.get(i).sendToServer("D");
		}
		for (int i=0; i<players.size(); i++)
		{
			String line = new String ();
			line =  
					players.get(i).getChips() + " " +
					players.get(i).isDealer() + " " +
					players.get(i).isSmallBlind() + " " +
					players.get(i).isBigBlind() + " " +
					players.get(i).getCardFromHand(0).getId() + " " +
					players.get(i).getCardFromHand(1).getId() + " " +
					players.get(i).getCardFromHand(2).getId() + " " +
					players.get(i).getCardFromHand(3).getId() + " " +
					players.get(i).getBet() + " " +
							pot + " " +
					players.get(i).getStatus() + " " +
							gRound + " " +
							showcards;
			//System.out.println("Sending pl infos: " + line + " to player nr: " + i);
			//System.out.println("Sending round number " + gRound);
			players.get(i).sendToServer(line);
			
		}
		//sending ifno about rest of players to all players - it will keep a sequence of adding players in client
		for (int i=0; i<players.size(); i++)
		{
			Player player_tmp = players.get(i).playerOnLeft;
			while (player_tmp != players.get(i))
			{
					String line = new String ();
					line =
							player_tmp.getChips() + " " +
							player_tmp.isDealer() + " " +
							player_tmp.isSmallBlind() + " " +
							player_tmp.isBigBlind() + " " +	//was it a mistake??? spradzic z kartami!!!!
							player_tmp.getCardFromHand(0).getId() + " " +
							player_tmp.getCardFromHand(1).getId() + " " +
							player_tmp.getCardFromHand(2).getId() + " " +
							player_tmp.getCardFromHand(3).getId() + " " +
							player_tmp.getBet() + " " +
							pot + " " +
							player_tmp.getStatus() + " " +
							gRound + " " +
									showcards;
							
					players.get(i).sendToServer(line);
					//System.out.println("Sending: " + line + " to player nr: " + i);
					//System.out.println("Sending round number " + gRound);
					player_tmp = player_tmp.playerOnLeft;
			}
			players.get(i).sendToServer("end");
		}
		
	}
	public void run()
	{
		try
		{
			/*ServerSocket*/ serverSocket = new ServerSocket(port);
			System.out.println("Starting Server at port: "+ port);
			System.out.println("Listening on port: " + serverSocket.getLocalPort());
			gRound = 1;
			//oczekiwanie na wszystkich graczy
			while(GlobalVariables.players_connected < GlobalVariables.players_number)
			{
				try
				{
					System.out.println("Waiting for getting socket from player number " + GlobalVariables.players_connected);
					clientSocket = serverSocket.accept();
					players.add(new Player(clientSocket, GlobalVariables.players_connected));
					players.get(GlobalVariables.players_connected).setNick(players.get(GlobalVariables.players_connected).getFromServer());
					System.out.println(players.get(GlobalVariables.players_connected).getNick());
					GlobalVariables.players_connected++;
					
					
				}
				catch(IOException e)
				{
					System.out.println(e);
				}
			}
			GlobalVariables.server_status = 1;
			
			System.out.println("Got enough player connected for the game. Let's start.");
			//launching player's clients
			for (int i=0; i<players.size(); i++)
			{
				System.out.println("sending to pl number " + i);
				players.get(i).sendToServer("ready");
				players.get(i).setChips(chipsPerPlayer);
			}
			//pointers to next plahyers
			for (int i=0; i < players.size()-1; i++)
			{
				players.get(i).playerOnLeft = players.get(i+1);
			}
			players.get(players.size()-1).playerOnLeft = players.get(0);
			//Setting players values
			Random generator = new Random();
			dealerId = generator.nextInt(GlobalVariables.players_number);
			players.get(dealerId).setDealer(true);
			players.get(dealerId).playerOnLeft.setSmallBlind(true);
			smallBlindId = players.get(dealerId).playerOnLeft.getPlayerId();
			players.get(dealerId).playerOnLeft.playerOnLeft.setBigBlind(true);
			bigBlindId = players.get(dealerId).playerOnLeft.playerOnLeft.getPlayerId();
			System.out.println("tokens:");
			for (int i=0; i<players.size(); i++)
			{
				System.out.print("pl number " + i);
				if (players.get(i).isDealer()) System.out.println(" is dealer");
				if (players.get(i).isBigBlind()) System.out.println(" is bigblind");
				if (players.get(i).isSmallBlind()) System.out.println(" is smallblind");
			}
			//sending start information (players nicks, etx
			for (int i=0; i<players.size(); i++)
			{
				Player player_tmp = players.get(i).playerOnLeft;
				while (player_tmp != players.get(i))
				{
						players.get(i).sendToServer(player_tmp.getNick() + " " + player_tmp.getPlayerId());
						player_tmp = player_tmp.playerOnLeft;
				}	
				players.get(i).sendToServer("end");
			}
			for (int i=0; i<players.size(); i++)
			{
				players.get(i).sendToServer(players.get(i).getPlayerId() + "");
			}
			//creating threads listening players actions
			ServerListenThread [] listenPlayer = new ServerListenThread[players.size()];
			for (int i=0; i<players.size(); i++)
			{
				listenPlayer[i] = new ServerListenThread(i);
				listenPlayer[i].start();
			}
			//here starts the main game
			while (GlobalVariables.players_connected > 1)
			{
				yield();
				showcards = 0;
				//starting 4-round thrives game
				deck = new Deck();
				//dealing cards to players
				for (int i=0; i<players.size(); i++)
				{
					for (int j=0; j<4; j++)
					{
						players.get(i).setCardInHand(j, deck.drawCardFromTop().getId());
					}
				}
				for (int i=0; i<players.size(); i++)
				{
					players.get(i).setBet(0);
					players.get(i).setFold(false);
					players.get(i).setAllin(false);
				}
				//here starts the main round!!!
				pot = 0;
				
				for (int round = 1; round <=4; round ++)
				{
					gRound = round;
					Player player_counter;
					if (round == 1)
					{
						for (int i=0; i<players.size(); i++)
						{
							if (players.get(i).isBigBlind())
							{
								playerBet(i, bigBlindBet, true, 1);
							}
							if (players.get(i).isSmallBlind())
							{
								playerBet(i, smallBlindBet, true, 1);
							}
						}
					}
					sendEverythingToClients();
					for (int i=0; i<players.size(); i++)
					{
						players.get(i).sendToServer("M 0");
					}
					//taking players before cause of moving to next player at the start of auction loop
					player_counter = players.get(0);
					if (round == 1)
					{
						player_counter = players.get(findBigBlind());
					}
					else
					{
						for (int x=0; x<players.size(); x++)
						{
							if (players.get(x).playerOnLeft == players.get(findSmallBlind())) 
							{
								player_counter = players.get(x);
							}
						}
						
					}
					//*********
					//AUCTION
					//*********
					for (int i=0; i<players.size(); i++)
					{
						players.get(i).sendToServer("M 0");
						players.get(i).setTurn(false);
					}
					someoneIsChanging = false;
					if (round==1) madeBet = true; else madeBet = false;
					System.out.println("Starting auction");
					around = false;
					Player player_around = player_counter;
					while (endOfAuction() == false)
					{
						sendEverythingToClients();
						for (int i=0; i<players.size(); i++)
						{
							System.out.println(players.get(i).getNick() + " f: "+ players.get(i).getFold()+
									" a:" + players.get(i).getAllin() + " b: "+ players.get(i).getBet()); 
							
						}
						player_counter = player_counter.playerOnLeft;
						while (player_counter.getFold() == true || player_counter.getAllin() == true) 
						{
							player_counter = player_counter.playerOnLeft;
							if (player_counter == player_around) around = true;
						}
						if (player_counter == player_around) around = true;
						if (player_counter.getFold()==true || player_counter.getAllin()==true) continue;
						player_counter.setTurn(true);
						someoneIsChanging = true;
						String s_out = new String();
						/*System.out.println("player bet: " + player_counter.getBet());
						System.out.println("current biggest bet: " + currentBiggestBet());
						System.out.println("players chips: " + player_counter.getChips());*/
						if (player_counter.getBet() == currentBiggestBet())
						{
							s_out = "T 1 1 "; //slider , check , bet/rise/allin
							if (madeBet == false) s_out+="0"; else s_out+="1";
						}
						else
						{
							if ((player_counter.getChips() + player_counter.getBet() <= currentBiggestBet()))
							{
								s_out = "T 0 0 2";
							}
							else
							{
								s_out = "T 1 0 1";

							}
						}
						s_out += " " + currentBiggestBet();
						//System.out.println("Turn message to client: " + s_out);
						player_counter.sendToServer(s_out);
						while (someoneIsChanging) 
						{
							yield();
						}
						System.out.println("Next player for auction");
						
						
					}
					//checking if just one in game and rest (all in , fold)
					int fold_counter = 0;
					for (int i=0; i<players.size(); i++)
					{
						if (players.get(i).getFold() == true) fold_counter++;
					}
					if (fold_counter == players.size()-1) break;
					System.out.println("Now cards exchange");
					//END OF AUCTION - CARDS EXCHANGE
					someoneIsChanging = false;
					for (int i=0; i<players.size(); i++)
					{
						players.get(i).sendToServer("M 1");
						players.get(i).setTurn(false);
					}
					player_counter = players.get(findDealer()).playerOnLeft;
					while (true) //nie da sie z warunkiem
					{
						sendEverythingToClients();
						if (player_counter.getFold() ==false) 
						{
							player_counter.sendToServer("CH");
							someoneIsChanging = true;
						}
						System.out.println("Waiting for change player: " + player_counter.getNick());
						while (someoneIsChanging) //stopping turn until selected player wont change his cards
						{
							yield();
						}
		
						player_counter = player_counter.playerOnLeft;
						if (player_counter == players.get(findDealer()).playerOnLeft)
						{
							break;
						}
						System.out.println("player changed");
					}
					sendEverythingToClients();
					System.out.println("Starting new round");
				}
				
				//checking players cards!!!
				//searching max of setup
				System.out.println("End of this part, checking results!");
				showcards = 1;
				sendEverythingToClients();
				//making sleep, to let players see the cards
				try
				{
					sleep(5000);
				}
				catch(InterruptedException e){}
				int max = 0;
				for (int i=0; i<players.size(); i++)
				{
					if (players.get(i).getFold() == false)
					{
						int a = players.get(i).playersHandValue();
						if (max == 0) max = a; else if (a>max) max = a;
					}
				}
				//searching how many has this setup (tie)
				int players_with_max = 0;
				for (int i=0; i<players.size(); i++)
				{
					if (players.get(i).getFold() == false )
					{
						if ( players.get(i).playersHandValue()==max) players_with_max++;
					}
				}
				for (int i=0; i<players.size(); i++)
				{
					if (players.get(i).getFold() == false )
					{
						if (players.get(i).playersHandValue() == max)
						{
							players.get(i).setChips(players.get(i).getChips()+(pot/players_with_max));
						}
					}
				}
				//giving tokens to next players
				boolean bb,sb,dl;
				bb=false;
				sb=false;
				dl=false;
				for (int i=0; i<players.size(); i++)
				{
					if (players.get(i).isBigBlind() && bb==false)
					{
						players.get(i).setBigBlind(false);
						players.get(i).playerOnLeft.setBigBlind(true);
						bb=true;
					}
					if (players.get(i).isSmallBlind() && sb==false)
					{
						players.get(i).setSmallBlind(false);
						players.get(i).playerOnLeft.setSmallBlind(true);
						sb=true;
					}
					if (players.get(i).isDealer() && dl==false)
					{
						players.get(i).setDealer(false);
						players.get(i).playerOnLeft.setDealer(true);
						dl=true;
					}
				}
				try
				{
					sleep(500);
				}
				catch(InterruptedException e){}
				for (int i=0; i<players.size();)
				{
					if (players.get(i).getChips() == 0)
					{
						int id_to_remove = players.get(i).getPlayerId();
						players.remove(i);
						for (int x=0; x<players.size(); x++)
						{
							players.get(x).sendToServer("Q " + id_to_remove);
						}
						continue;
					}
					i++;
				}
			}
			
	
		}
		catch(IOException ioe)
		{
			System.out.println("Couldn't create a server on port: "+port);
			System.exit(-1);
		}
	}
	private class ServerListenThread extends Thread
	{
		private Player player;
		private int index;
		private boolean playerExists;
		public void setPlayer(boolean b)
		{
			playerExists = b;
		}
		ServerListenThread(int i)
		{
			player = players.get(i);
			index = i;
			playerExists = true;
		}
		//listening to the clients and reacts
		public synchronized void run()
		{
			while (playerExists == true)
			{
				yield();
				if (playerExists==false) break;
				//System.out.println("Starting to listen");
				String threadLine = new String();
				threadLine = player.getFromServer();
				String [] splitStr = threadLine.split(" ");
				//************************************
				//removing player form clients memory
				//*************************************
				if (threadLine.equals("Q")) //do zrobienia big blindy - zrobione
				{
					System.out.println("listened Q");
					
					int index = 0;
					//for (int i=0; i< players.size(); i++)
					//{
						if (player.isDealer())
						{
							for (int x=0 ; x<players.size(); x++)
							{
								if (players.get(x).playerOnLeft == player) players.get(x).setDealer(true);
							}
						}
						if (player.isSmallBlind())
						{
							player.playerOnLeft.setSmallBlind(true);
							player.playerOnLeft.playerOnLeft.setBigBlind(true);
						}
						if (player.isBigBlind())
						{
							player.playerOnLeft.setBigBlind(true);
						}
						for (int x=0; x<players.size(); x++)
						{
							if (players.get(x).playerOnLeft == player) players.get(x).playerOnLeft = player.playerOnLeft;
							if (player != players.get(x))
							{
								players.get(x).sendToServer("Q " + player.getPlayerId());
							}
							else
							{
								index = x;
							}
						}
						/*if (player != players.get(i))
						{
							players.get(i).sendToServer("Q " + player.getPlayerId());
						}*/
					//}
					player.closeConnection();
					players.remove(index);
					GlobalVariables.players_connected--;
					//System.gc();
					sendEverythingToClients();
					
					playerExists = false;
					System.out.println("Breaking listening thread");
					return;
				}
				if (splitStr[0].equals("R"))
				{
					System.out.println("Received return message auction with");
					//Fold check bet call
					if (splitStr[1].equals("F"))
					{
						System.out.println("Fold");
						player.setFold(true);
						for (int i=0; i<4; i++)
						{
							deck.appendCardToDeck(player.getCardFromHand(i));
						}
					}
					//searching player index
					int index=0;
					for (int i=0; i<players.size(); i++)
					{
						if (players.get(i) == player) index = i;
					}
					if (splitStr[1].equals("C"))
					{
						System.out.println("Call");
						playerCall(index);
					}
					if (splitStr[1].equals("R") || splitStr[1].equals("B"))
					{
						System.out.println("Raise/bet");
						int bet_now = Integer.parseInt(splitStr[2]);
						int t = 0;
						if (splitStr[1].equals("R")) t = 2; else t = 1;
						playerBet(index, bet_now, false, t);
					}
					if (splitStr[1].equals("A"))
					{
						System.out.println("All-in");
						playerAllin(index);
						
					}
					if (splitStr[1].equals("H"))
					{
						System.out.println("Check");
						players.get(index).setCheck(true);
						players.get(index).setStatus(5);
					}
					someoneIsChanging = false;
					//sendEverythingToClients();
				}
				if (splitStr[0].equals("C") || threadLine.equals("C"))
				{
					System.out.println("change: ");
					someoneIsChanging = false;
					if (splitStr.length <= 1){System.out.println("no cards to change, exit"); continue;}
					System.out.println("changing cards");
					int [] card_pos = new int [splitStr.length-1];
					for (int i=1; i<splitStr.length; i++)
					{
						card_pos[i-1] = Integer.parseInt(splitStr[i]);
					}
					//changing cards one by one because of shuffling when its full of old cards
					for (int i=0; i<card_pos.length; i++)
					{
						deck.appendCardToDeck(player.getCardFromHand(card_pos[i]));
						player.setCardInHand(card_pos[i], deck.drawCardFromTop().getId());
					}
				}
				
			}
		}
	}
	
}
